<template>
  <div class="input">
    <input type="text" v-model="imageInfo.url" />
    <button @click="getWidthHeight(imageInfo.url)">生成</button>
  </div>
  <div class="fragmentation">
    <div
      id="box"
      class="box"
      :style="{
        width: imageInfo.width + 'px',
        height: imageInfo.height + 'px',
      }"
    ></div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref } from "vue";
// https://pic.netbian.com/uploads/allimg/240714/235440-17209724800aad.jpg
const imageInfo = ref({
  url: "https://pic.3gbizhi.com/uploads/20240712/3782a48bd0f9e19a3363dcae9f0d8237.jpg",
  width: 1920,
  height: 1080,
});
const getRandom = (min: number, max: number) =>
  Math.floor(Math.random() * (max - min + 1) + min);

function removeAllChildren(parentElement: HTMLElement) {
  while (parentElement.firstChild) {
    parentElement.removeChild(parentElement.firstChild);
  }
}
function domAppendChild(url: string, row: number, col: number) {
  const box = document.getElementById("box");
  if (!box) return;
  removeAllChildren(box);
  // 定义多少个小块，由多少行和列决定

  // 计算小块的宽高
  const smallBoxWidth = imageInfo.value.width / col;
  const smallBoxHeight = imageInfo.value.height / row;
  /** @name 创建小块 **/
  function createSmallBox() {
    for (let i = 0; i < row; i++) {
      for (let j = 0; j < col; j++) {
        const smallBox = document.createElement("div");

        // 设置背景偏移量，让小块的背景显示对应图片的位置，和以前那种精灵图一样
        const offsetX = j * smallBoxWidth * -1;
        const offsetY = i * smallBoxHeight * -1;
        smallBox.style.backgroundPosition = `${offsetX}px ${offsetY}px`;
        smallBox.style.backgroundSize = `${imageInfo.value.width}px ${imageInfo.value.height}px`;

        smallBox.classList.add("small-box");
        smallBox.style.width = smallBoxWidth + "px";
        smallBox.style.height = smallBoxHeight + "px";
        smallBox.style.backgroundImage = `url(${url})`;

        // 给每个小块增加不同的延时，让动画不同时间执行
        // const delay = i * 100; // 延迟时间为毫秒(ms)，注意不要太小了
        const delay = getRandom(0, col + row) * 100;

        smallBox.style.animationDelay = `${delay}ms`;

        // 相邻两个小块旋转相反
        const contrary = (i + j) % 2 === 0;
        smallBox.style.setProperty(
          "--rotateX",
          `rotateX(${contrary ? -180 : 0}deg)`
        );
        smallBox.style.setProperty(
          "--rotateY",
          `rotateY(${contrary ? 0 : -180}deg)`
        );

        // 优化渲染，并解决有可能出现的白边问题
        smallBox.style.willChange = "transform";
        // 在动画执行后，需要重置will-change
        const timer = setTimeout(() => {
          smallBox.style.willChange = "initial";
          clearTimeout(timer);
        }, 2000);

        // 插入小块
        box?.appendChild(smallBox);
      }
    }
  }
  createSmallBox();
}
function gcd(a: number, b: number) {
  if (b === 0) {
    return a;
  }
  return gcd(b, a % b);
}

function findGCDNotGreaterThan20(num: number) {
  let maxGCD = 1;
  for (let i = 1; i <= 20; i++) {
    if (num % i === 0) {
      maxGCD = Math.max(maxGCD, gcd(num, i));
    }
  }
  return maxGCD;
}

function getWidthHeight(url: string) {
  if (!url) return;
  // 创建一个新的 Image 对象
  var img = new Image();
  // 设置图片的 src 属性
  img.src = url;
  // 当图片加载完成后，获取图片的宽高比
  img.onload = function () {
    const row = findGCDNotGreaterThan20(img.height);
    const col = findGCDNotGreaterThan20(img.width);
    imageInfo.value.width = img.width;
    imageInfo.value.height = img.height;
    domAppendChild(url, row, col);
  };
}
onMounted(() => {
  // getWidthHeight(imageInfo.value.url);
});
</script>

<style lang="scss" scoped>
.fragmentation {
  width: 100%;
  height: 100vh;
  padding: 0;
  margin: 0;
  display: flex;
  justify-content: center;
  align-items: center;
}

.box {
  display: flex;
  /* 小块自动换行排列 */
  flex-wrap: wrap;
  justify-content: center;
}

.input {
  position: fixed;
  top: 10px;
  left: 10px;
}
</style>
<style lang="scss">
.small-box {
  background-size: cover;

  background-repeat: no-repeat;
  opacity: 0;
  animation: smallBoxAnimate 2000ms linear forwards;

  --rotateX: rotateX(0);
  --rotateY: rotateY(0);
  transform: var(--rotateX) var(--rotateY) scale(0.8);
}
@keyframes smallBoxAnimate {
  0% {
    opacity: 0;
  }
  40% {
    opacity: 0;
  }
  70% {
    opacity: 1;
  }
  100% {
    opacity: 1;
  }
}
@keyframes smallBoxAnimate {
  0% {
    opacity: 0;
    transform: var(--rotateX) var(--rotateY) scale(0.8);
  }
  40% {
    opacity: 0;
    transform: var(--rotateX) var(--rotateY) scale(0.8);
  }
  70% {
    opacity: 1;
    transform: rotateX(0) rotateY(0) scale(0.8);
  }
  100% {
    opacity: 1;
    transform: rotateX(0) rotateY(0) scale(1);
  }
}
</style>
